
"""
Kaggle比赛
房价预测数据集
"""
import d2l.torch
from torch.utils import data
import pandas as pd
import torch
import torch.nn as nn
import toolpy
import numpy as np

loss = nn.MSELoss() #使用均方误差

def load_house_price_data(train_fp=r"D:\Study\d2l-zh\data\house-prices-advanced-regression-techniques\train.csv", test_fp=r"D:\Study\d2l-zh\data\house-prices-advanced-regression-techniques\test.csv"):
    "读取房价预测数据， 并且预处理数据"
    train_features = pd.read_csv(train_fp)
    test_features = pd.read_csv(test_fp)
    test_labels = pd.read_csv(r"D:\Study\d2l-zh\data\house-prices-advanced-regression-techniques\sample_submission.csv")

    all_features = pd.concat((train_features.iloc[:,1:-1], test_features.iloc[:, 1:]))

    '处理数值部分'
    numeric_features = all_features.dtypes[all_features.dtypes != 'object'].index
    all_features[numeric_features] = all_features[numeric_features].apply(lambda x: ((x-x.mean())/x.std())) #将所有数值部分标准化
    all_features[numeric_features] = all_features[numeric_features].fillna(0) #对于nan的数据用0填写

    '处理离散值，将离散值变为独热编码'
    all_features = pd.get_dummies(all_features, dummy_na=True)

    n_train = len(train_features)
    train_labels = torch.tensor(train_features.SalePrice.values.reshape(-1, 1), dtype=torch.float32)
    train_features = torch.tensor(all_features[:n_train].values, dtype=torch.float32)
    test_features = torch.tensor(all_features[n_train:].values, dtype=torch.float32)
    test_labels = torch.tensor(test_labels.iloc[:,1].values, dtype=torch.float32)


    return train_features, train_labels, test_features, test_labels

def log_rmse(net, features, labels):
    "损失函数, 对数均方根误差"
    clipped_preds = torch.clamp(net(features), 1, float('inf'))
    rmse = torch.sqrt(loss(torch.log(clipped_preds), torch.log(labels)))
    return rmse.item() #rmse是误差对象

class HNet1(nn.Module):
    "第一代网络版本，一层连接层"
    def __init__(self, in_features=331):
        super().__init__()
        self.fc1 = nn.Linear(in_features, 1)

    def forward(self, x):
        return self.fc1(x)

def train_house_price(net, train_features, train_labels, test_features, test_labels, num_epochs, lr, weight_decay, batch_size):
    train_ls, test_ls = [], [] #误差
    train_iter = d2l.torch.load_array((train_features, train_labels), num_epochs)
    # 使用Adam优化算法
    optimizer = torch.optim.Adam(net.parameters(), lr = lr, weight_decay=weight_decay)

    for epoch in range(num_epochs):
        for X,y in train_iter:
            optimizer.zero_grad()
            l = loss(net(X), y)
            l.backward()
            optimizer.step()
        train_ls.append(log_rmse(net, train_features, train_labels))
        test_ls.append(log_rmse(net, test_features, test_labels))
        print("Epoch %d， train rmse %.4f, test rmse %.4f" %(epoch+1, train_ls[-1], test_ls[-1]))
    toolpy.plot(np.arange(1,num_epochs+1), train_ls, 'epoch', 'log rmse')
    return train_ls, test_ls

def pred_test(net, test_features):
    test_data = pd.read_csv(r"D:\Study\d2l-zh\data\house-prices-advanced-regression-techniques\sample_submission.csv")
    preds = net(test_features).detach().numpy()
    test_data['SalePrice'] = preds
    test_data.to_csv(r"D:\Study\d2l-zh\data\house-prices-advanced-regression-techniques\my_submission.csv", index=False)
    return

train_features, train_labels, test_features, test_labels = load_house_price_data()
net = HNet1()

num_epochs, lr, weight_decay, batch_size = 200, 5, 0, 64

train_house_price(net, train_features, train_labels, test_features, test_labels, num_epochs, lr, weight_decay, batch_size)
pred_test(net, test_features)




